package com.solveit.aps.io.db;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import com.solveit.core.configuration.SystemConfiguration;
import com.solveit.core.db2.caching.EntityCollection;
import com.solveit.core.db2.generator.MetaClass;
import com.solveit.core.db2.generator.PrimaryKey;
import com.solveit.core.db2.util.Maintain;

public class StaticEntityContainer {

	private static StaticEntityContainer instance;
	private Map<Class<?>, EntityCollection<?>> collections = new HashMap<Class<?>, EntityCollection<?>>();

	public StaticEntityContainer() {
		// the order does not matter.		
		// load UAC relevant data
		try {
//			UacDataModel.getInstance().load(InternalDbManager.getInstance());
			for (String className: SystemConfiguration.getInstance().getNumberedParametersStartingWith("StartupCollection")){
				Class<?> entityClass = Class.forName(className);
				Class<?> entityCollectionClass = Class.forName(className+"Collection");
				Method getIntanceMethod = entityCollectionClass.getMethod("getInstance");
				EntityCollection<?> entityCollection = (EntityCollection<?>)getIntanceMethod.invoke(null);
				collections.put(entityClass, entityCollection);
			}
			
			for (String className: SystemConfiguration.getInstance().getNumberedParametersStartingWith("NonStartupCollection")){
				if (className.indexOf(',')>0){
					className = className.substring(0, className.indexOf(',')).trim();
				}
				Class<?> entityClass = Class.forName(className);
				@SuppressWarnings({ "unchecked", "rawtypes" })
				EntityCollection<?> entityCollection = new EntityCollection(entityClass);
				collections.put(entityClass, entityCollection);
			}
			
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public void loadStatics() {
		Class<?>[] unsorted = collections.keySet().toArray(new Class<?>[collections.size()]);
		Class<?>[] sorted = Maintain.sortByDependency(false, unsorted);
		for (int i = sorted.length - 1; i >= 0; i--) {
			Class<?> cl = sorted[i];
			EntityCollection<?> collection = collections.get(cl);
			if (collection instanceof Initializable) {
				((Initializable) collection).loadAtStartup();
			}
		}
	}

	public static void create() {
		instance = new StaticEntityContainer();
	}

	public static StaticEntityContainer getInstance() {
		return instance;
	}

	/**
	 * Global repository for all singleton database entities (those that are
	 * loaded at startup and never loaded again).
	 * 
	 * @param target
	 * @param type
	 * @param meta
	 * @param foreignKey
	 * @return
	 */
	public Object retrieve(final Object target, final Class<?> type, final MetaClass meta, final PrimaryKey foreignKey) {
		assert target.getClass() == meta.getClazz();
		
		final EntityCollection<?> coll = getCollection(type);
		assert coll != null : "Could not find entity collection for type " + type + ". Please register in StaticEntityContainer constructor.";
		
		if (coll != null) {
			final Object record = coll.getOrCreate(foreignKey);
			return record;
		}

		return null;
	}

	/**
	 * Put object into the right entity collection (if you want to, i.e. if the
	 * object is a singleton database entity).
	 * 
	 * @param record
	 */
	public Object store(Object record) {
		final Class<?> cl = record.getClass();

		final EntityCollection<?> coll = getCollection(cl);
		if (coll != null) {
			record = coll.addUnsafe(record);
		}
		return record;
	}

	@SuppressWarnings("unchecked")
	public <E> EntityCollection<E> getCollection(final Class<E> clazz) {
		return (EntityCollection<E>) collections.get(clazz);
	}

	public boolean containsKey(Class<?> clazz){
		return collections.containsKey(clazz);
	}
	
	public synchronized Map<Class<?>, EntityCollection<?>> getCollections() {
		return collections;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public synchronized Map<Class<?>, EntityCollection<?>> copyCollections() {
		Map<Class<?>, EntityCollection<?>> copy = new HashMap<Class<?>, EntityCollection<?>>();
		for (EntityCollection<?> collection : this.getCollections().values()) {
			EntityCollection<?> collectionCopy = new EntityCollection(collection.getType());
			for (Object obj : collection.getValues()) {
				collectionCopy.addUnsafe(obj);
			}
			for (Object obj : collection.getDeletedValues()) {
				collectionCopy.addUnsafe(obj);
			}
			copy.put(collection.getType(), collectionCopy);
		}
		return copy;
	}
}
